package  
{
	import flash.geom.Vector3D;
	import starling.core.Starling;
	import starling.display.Sprite;
	import starling.events.Event;
	import starling.filters.BlurFilter;
	
	public class Game extends Sprite
	{
		private const MAX_BOID:int = 4000;
		
		private var mStageWidth:int 		= 0;
		private var mStageHeight:int 		= 0;
		private var mScreenCenterX:Number 	= 0;
		private var mScreenCenterY:Number 	= 0;
		private var mBoidList:Vector.<Boid> = new Vector.<Boid>(MAX_BOID, true);
		private var mColorModifierList:Vector.<BoidColorModifier> = new Vector.<BoidColorModifier>();
		
		public function Game() 
		{
			mStageWidth = Starling.current.stage.stageWidth;
			mStageHeight = Starling.current.stage.stageHeight;
			mScreenCenterX = mStageWidth / 2;
			mScreenCenterY = mStageHeight / 2;
			this.alpha = 0.999; // Optimization
			this.touchable = false;
			Rndm.seed = int.MAX_VALUE * Math.random();
			this.filter = new BlurFilter();
			CreateBoids();
			CreateColorModifier();
			
			addEventListener(Event.ENTER_FRAME, OnEnterFrame);
		}
		
		private function CreateBoids():void
		{
			// Create boids
			for (var i:int = 0; i < MAX_BOID; i++)
			{
				mBoidList[i] = new Boid(this);
			}
			
			// Assign
			for (i = 0; i < MAX_BOID; i++)
			{
				Assign(mBoidList[i], i);
				mBoidList[i].SetBindPos(mScreenCenterX, mScreenCenterY, mScreenCenterY);
				RandomPos(mBoidList[i]);
			}
		}
		
		private function CreateColorModifier():void
		{
			// Red
			mColorModifierList.push(new BoidColorModifier(new Vector3D(mStageWidth * 0.25, mStageHeight * 0.75, mStageHeight * 0.50), 0xFF0000));
			
			// Yellow
			mColorModifierList.push(new BoidColorModifier(new Vector3D(mStageWidth * 0.25, mStageHeight * 0.25, mStageHeight * 0.50), 0xFFF000));
			
			// Blue
			mColorModifierList.push(new BoidColorModifier(new Vector3D(mStageWidth * 0.75, mStageHeight * 0.25, mStageHeight * 0.50), 0x0000FF));
			
			// Green
			mColorModifierList.push(new BoidColorModifier(new Vector3D(mStageWidth * 0.75, mStageHeight * 0.75, mStageHeight * 0.50), 0x00FF00));
		}
		
		private function Assign(aBoid:Boid, aIndex:int):void
		{
			var randomFriend1:int = 0;
			do
			{
				randomFriend1 = RandomInt(0, MAX_BOID - 1);
			}
			while (aIndex == randomFriend1)
			
			var randomFriend2:int = 0;
			do
			{
				randomFriend2 = RandomInt(0, MAX_BOID - 1);
			}
			while (aIndex == randomFriend2 || randomFriend1 == randomFriend2)
			
			aBoid.mFriend = mBoidList[randomFriend1];
			aBoid.mEnemy = mBoidList[randomFriend2];
		}
		
		private var mFrameCpt:int = 10;
		private function OnEnterFrame(aEvent:Event):void
		{
			for (var i:int = 0; i < MAX_BOID; i++)
			{
				mBoidList[i].Update();
			}
			
			mFrameCpt--;
			
			if (mFrameCpt <= 0)
			{
				mFrameCpt = 10;
				
				var rnd:int = RandomInt(0, MAX_BOID - 1);
				Assign(mBoidList[rnd], rnd);
				RandomPos(mBoidList[rnd]);
				mBoidList[rnd].mColor = 0;
			}
			
			// Apply color modifier
			for (i = 0; i < mColorModifierList.length; i++)
			{
				for (var j:int = 0; j < MAX_BOID; j++)
				{
					mColorModifierList[i].ComputeBoid(mBoidList[j]);
					
					// If the boid's color is too dark, re-inject
					if (mBoidList[j].IsTooDark())
					{
						Assign(mBoidList[j], j);
						RandomPos(mBoidList[j]);
						mBoidList[j].mColor = 0;
					}
				}
			}
		}
		
		private function RandomInt(aMin:int, aMax:int):int
		{
			return (Math.floor(Rndm.random() * (aMax - aMin + 1)) + aMin); 
		}
		
		private function RandomNumber(aMin:Number, aMax:Number):Number
		{
			return aMin + (Rndm.random() * (aMax - aMin));
		}
		
		private function RandomPos(aBoid:Boid):void
		{
			aBoid.mPos.x = RandomNumber(0, mStageWidth);
			aBoid.mPos.y = RandomNumber(0, mStageHeight);
			aBoid.mPos.z = RandomNumber(0, mStageHeight);
		}
	}
}